package com.mrtaohuan.render.freemarker;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.logging.Level;

import javax.servlet.http.HttpServletResponse;

import lombok.extern.java.Log;

import org.apache.commons.lang.time.DateFormatUtils;

import com.mrtaohuan.constant.Constants;
import com.mrtaohuan.render.HttpContext;
import com.mrtaohuan.render.IHttpResponseRender;

import freemarker.template.Template;

@Log
public abstract class AbstractFreeMarkerRender implements IHttpResponseRender {

	private HttpContext context;

	/**
	 * Template name.
	 */
	private String templateName;

	/**
	 * Data model.
	 */
	private Map<String, Object> dataModel = new HashMap<String, Object>();

	/**
	 * Gets a template with the specified template directory name and template
	 * name.
	 * 
	 * @param templateDirName
	 *            the specified template directory name
	 * @param templateName
	 *            the specified template name
	 * @return template
	 * @throws IOException
	 *             io exception
	 */
	protected Template getTemplate(final String templateDirName,
			final String templateName) throws IOException {
		return Templates.getTemplate(templateDirName, templateName);
	}

	/**
	 * Invoked before render.
	 * 
	 * @param context
	 *            the specified context
	 * @throws Exception
	 *             exception
	 */
	protected abstract void beforeRender(final HttpContext context)
			throws Exception;

	/**
	 * Invoked after render.
	 * 
	 * @param context
	 *            the specified context
	 * @throws Exception
	 *             exception
	 */
	protected abstract void afterRender(final HttpContext context)
			throws Exception;

	@Override
	public void render() {
		final HttpServletResponse response = context.getResponse();
		response.setContentType("text/html");
		response.setCharacterEncoding("UTF-8");
		PrintWriter writer;

		try {
			writer = response.getWriter();
		} catch (final Exception e) {
			try {
				writer = new PrintWriter(response.getOutputStream());
			} catch (final IOException ex) {
				log.log(Level.SEVERE, "Can not get response writer", ex);
				return;
			}
		}

		if (response.isCommitted()) { // response has been sent redirect
			writer.flush();
			writer.close();
			return;
		}

		try {

			final Template template = getTemplate(Constants.TEMPLETE_DIR,
					templateName);

			// dataModel.put(Keys.REQUEST, request);
			// Keys.fillServer(dataModel);

			beforeRender(context);

			final String html = genHTML(dataModel, template);

			doRender(html, context);

			afterRender(context);

		} catch (final Exception e) {
			log.log(Level.SEVERE, "FreeMarker renders error", e);

			try {
				response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			} catch (final IOException ex) {
				log.log(Level.SEVERE, "Can not send error 500!", ex);
			}
		}

	}

	@Override
	public String renderHtml() {

		Template template;
		try {
			template = getTemplate(Constants.TEMPLETE_DIR, templateName);
			return genHTML(dataModel, template);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * Processes the specified FreeMarker template with the specified request,
	 * data model.
	 * 
	 * @param request
	 *            the specified request
	 * @param dataModel
	 *            the specified data model
	 * @param template
	 *            the specified FreeMarker template
	 * @return generated HTML
	 * @throws Exception
	 *             exception
	 */
	protected String genHTML(final Map<String, Object> dataModel,
			final Template template) throws Exception {
		final StringWriter stringWriter = new StringWriter();

		template.setOutputEncoding("UTF-8");
		template.process(dataModel, stringWriter);

		final StringBuilder pageContentBuilder = new StringBuilder(
				stringWriter.toString());

		final long endtimeMillis = System.currentTimeMillis();

		final String dateString = DateFormatUtils.format(endtimeMillis,
				"yyyy/MM/dd HH:mm:ss", TimeZone.getTimeZone("GMT+8"));
		final long starttimeMillis = (Long) context.getRequest().getAttribute(
				"START_TIME_MILLIS");

		final String msg = String
				.format("<!-- Generated by Freemarker,Powered by Tao Huan,Generated date %1$s,Processed %2$sms -->",
						dateString,
						endtimeMillis - starttimeMillis);
		pageContentBuilder.append(msg);

		final String ret = pageContentBuilder.toString();

		return ret;
	}

	protected void doRender(final String html, final HttpContext context)
			throws Exception {
		PrintWriter writer;

		try {
			writer = context.getResponse().getWriter();
		} catch (final Exception e) {
			writer = new PrintWriter(context.getResponse().getOutputStream());
		}

		if (context.getResponse().isCommitted()) {
			writer.flush();
			writer.close();
			return;
		}

		writer.write(html);
		writer.flush();
		writer.close();
	}

	/**
	 * Gets the data model.
	 * 
	 * @return data model
	 */
	public Map<String, Object> getDataModel() {
		return dataModel;
	}

	/**
	 * Gets the template name.
	 * 
	 * @return template name
	 */
	public String getTemplateName() {
		return templateName;
	}

	/**
	 * Sets the template name with the specified template name.
	 * 
	 * @param templateName
	 *            the specified template name
	 */
	public void setTemplateName(final String templateName) {
		this.templateName = templateName+Constants.TEMPLETE_SUFFIX;
	}

	@Override
	public Map<String, Object> getRenderDataModel() {
		return dataModel;
	}

	public HttpContext getContext() {
		return context;
	}

	public void setContext(HttpContext context) {
		this.context = context;
	}

}
